/*-
 * Copyright 2013 Bang Jun-young.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer,
 *    without modification, immediately at the beginning of the file.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

//
// Vendor-independent C++ Property Version 2.0
//

/* USAGE EXAMPLE
#include <iostream>
#include <property.h>

class Car {
    using_property(Car);

public:
    property(int, Speed)
        int getSpeed() {
            return speed;
        }
        void setSpeed(int value) {
            speed = (value > 300) ? 300 : value;
        }
    end_property

    readonly_property(int, Rpm)
        int getRpm() {
            return speed * 10;
        }
    end_property

private:
    int speed;
};

int main() {
    using namespace std;

    Car car1, car2;
    car1.Speed = 50;
    car2.Speed = car1.Speed;
    car1.Speed = car2.Speed = 200;
    car1.Speed = 100;
    ++car1.Speed;
    car2.Speed--;
    int rpm = car1.Rpm;
#if 0
    car1.Rpm = 2000;        // Compile error: read-only property
#endif
    cout << car1.Speed << endl;
    cout << car1.Rpm << endl;
    cout << car2.Speed << endl;

    return 0;
}
*/

#ifdef _MSC_VER
#pragma once
#endif

#ifndef _MOGUA_PROPERTY_H_
#define _MOGUA_PROPERTY_H_

#include <stddef.h>     // for offsetof() macro

#define using_property(Class) \
        typedef Class TClass

#define readonly_property_class(TProp, Prop) \
        class ReadOnlyProperty_##Prop { \
        public: \
            /* Constructor */ \
            ReadOnlyProperty_##Prop() { \
                Init(offsetof(TClass, Prop)); \
            } \
        \
            /* Function for assignment from the property */ \
            operator TProp() { \
                return instance->get##Prop(); \
            } \
        \
        protected: \
            void Init(size_t offset) { \
                instance = reinterpret_cast<TClass*>(reinterpret_cast<char*>(this) - offset); \
            } \
        \
            TClass* instance; \
        }

#define property_class(TProp, Prop) \
        class Property_##Prop : public ReadOnlyProperty_##Prop { \
        public: \
            /* Constructor */ \
            Property_##Prop() { \
                Init(offsetof(TClass, Prop)); \
            } \
        \
            /* Function for assignment to the property */ \
            TProp operator =(const TProp& rhs) { \
                instance->set##Prop(rhs); \
                return rhs; \
            } \
        \
            /* Function for assignment between properties of the same type (and name) */ \
            Property_##Prop& operator =(const Property_##Prop& rhs) { \
                instance->set##Prop(rhs.instance->get##Prop()); \
                return *this; \
            } \
        \
            /* Prefix increment operator */ \
            Property_##Prop& operator ++() { \
                TProp value = instance->get##Prop(); \
                ++value; \
                instance->set##Prop(value); \
                return *this; \
            } \
        \
            /* Postfix increment operator */ \
            TProp operator ++(int) { \
                TProp temp, value; \
                temp = value = instance->get##Prop(); \
                value++; \
                instance->set##Prop(value); \
                return temp; \
            } \
        \
            TProp operator +=(TProp rhs) { \
                TProp value; \
                value = instance->get##Prop(); \
                value += rhs; \
                instance->set##Prop(value); \
                return value; \
            } \
        \
            /* Prefix decrement operator */ \
            Property_##Prop& operator --() { \
                TProp value = instance->get##Prop(); \
                --value; \
                instance->set##Prop(value); \
                return *this; \
            } \
        \
            /* Postfix decrement operator */ \
            TProp operator --(int) { \
                TProp temp, value; \
                temp = value = instance->get##Prop(); \
                value--; \
                instance->set##Prop(value); \
                return temp; \
            } \
        \
            TProp operator -=(TProp rhs) { \
                TProp value; \
                value = instance->get##Prop(); \
                value -= rhs; \
                instance->set##Prop(value); \
                return value; \
            } \
        }

#define readonly_property(TProp, Prop) \
        readonly_property_class(TProp, Prop); \
        ReadOnlyProperty_##Prop Prop;

#define property(TProp, Prop) \
        readonly_property_class(TProp, Prop); \
        property_class(TProp, Prop); \
        Property_##Prop Prop;

#define end_property

#endif // !_MOGUA_PROPERTY_H_
